type msg =
  | Deconnexion
  | ErreurProtocole
  | Refuse
  | Identifier of string
  | Inviter of string array
  | Entree of string
  | Sortie of string
  | Noms of string array
  | Numero of int
  | Distribution of Carte.carte array
  | Prise of int
  | Contrat of int
  | Appel
  | Appeler of Carte.carte
  | ContratFinal of int * int * Carte.carte (*preneur - prise - appel *)
  | Chien of Carte.carte array
  | Ecart of Carte.carte array
  | Atout of Carte.carte list
  | Jeu
  | Requete of Carte.carte
  | Carte of Carte.carte
  | Pli of int (* le joueur *)
  | Resultat of int array

let ecrire_message msg = 
  let creer_chaine chaine =
    Array.init 16 (fun i -> if i < String.length chaine 
			    then int_of_char chaine.[i]
			    else 0)
  in
  match msg with
  | Deconnexion -> failwith "Je ne sais pas écrire déconnexion"
  | ErreurProtocole -> [|2 ; 0|]
  | Refuse -> [|2 ; 1|]
  | Identifier chaine -> 
     Array.append [|18 ; 20|] (creer_chaine chaine)
  | Inviter personnes ->
     Array.concat ([| 4*16 + 2 ; 24|] ::
		     (Array.to_list
			(Array.init 4 (fun i -> creer_chaine personnes.(i)))))
  | Entree personne ->
     Array.append [|18 ; 21|] (creer_chaine personne)
  | Sortie personne ->
     Array.append [|18 ; 22|] (creer_chaine personne)
  | Noms personnes ->
     Array.concat ([| 5*16 + 2 ; 23|] ::
		     (Array.to_list
			(Array.init 5 (fun i -> creer_chaine personnes.(i)))))
  | Numero n -> [|3 ; 2 ; n|]
  | Distribution cartes ->
     Array.append [|15 + 2 ; 3|] 
		  (Array.init 15 (fun i -> Carte.int_of_carte cartes.(i)))
  | Prise x -> [|3 ; 4 ; x|]
  | Contrat x -> [|3 ; 5 ; x|]
  | Appel -> [|2 ; 6|]
  | Appeler c -> [|3 ; 7 ; Carte.int_of_carte c|]
  | ContratFinal (preneur, prise, carte) -> 
     [|5 ; 8 ; preneur ; prise ; Carte.int_of_carte carte |]
  | Chien cartes ->
     let c = Array.map (Carte.int_of_carte) cartes in
     [|5 ; 9 ; c.(0) ; c.(1) ; c.(2)|]
  | Ecart cartes ->
     let c = Array.map (Carte.int_of_carte) cartes in
     [|5 ; 10 ; c.(0) ; c.(1) ; c.(2)|]
  | Atout liste ->
     let c = Array.of_list (List.map (Carte.int_of_carte) liste) in
     Array.append [|2 + Array.length c ; 11|] c
  | Jeu -> [|3 ; 13 ; 0|]
  | Requete c -> [|3 ; 16 ; Carte.int_of_carte c|]
  | Carte c -> [|3 ; 17 ; Carte.int_of_carte c|]
  | Pli x -> [|3 ; 18 ; x|]
  | Resultat res ->
     let rec decouper x =
       if x < 0 then
	 decouper ((1 lsl 17) + x)
       else 
	 let oc1 = (x lsr 8) land 0b11111111 in
	 let oc2 = x land 0b11111111 in
	 [|oc1 ; oc2|]
     in
     let liste = [|2*5+2 ; 19|]
		 :: (Array.to_list
		       (Array.map (decouper) res)) in
     Array.concat liste

let lire_message () =
  let octets = ref [||] in
  let i = ref 0 in
  let mk_string i =
    let tab = Array.sub !octets i 16 in
    let caracteres = Array.map (char_of_int) tab in
    let chaine = String.make 16 (char_of_int 0) in
    let rec recopier i =
      if i < Array.length caracteres then
	if int_of_char caracteres.(i) = 0 then ()
	else
	  let () = String.set chaine i caracteres.(i) in
	  recopier (i + 1)
      else ()
    in
    let () = recopier 0 in
    chaine
  in
  let ajouter_entier x =
    let () = 
      if Array.length !octets = 0 then
	(* On obtient la taille *)
	let () = octets := Array.make (x - 1) 0 in
	i := 0
      else (*if !i < Array.length !octets then *)
	let () = (!octets).(!i) <- x in
	incr i
    in
    if !i = Array.length !octets then (* Le message est reçu *)
      let reponse =
	try
	  match (!octets).(0) with
	  | 0 -> Some ErreurProtocole
	  | 1 -> Some Refuse
	  | 2 -> Some (Numero (!octets).(1))
	  | 3 -> Some (Distribution 
			 (Array.map (Carte.carte_of_int)
				    (Array.sub !octets 1 15)))
	  | 4 -> Some (Prise (!octets).(1))
	  | 5 -> Some (Contrat (!octets).(1))
	  | 6 -> Some Appel
	  | 7 -> Some (Appeler (Carte.carte_of_int (!octets).(1)))
	  | 8 -> Some (ContratFinal 
			 ((!octets).(1),
			  (!octets).(2),
			  Carte.carte_of_int (!octets).(3)))
	  | 9 -> Some (Chien 
			 (Array.map (Carte.carte_of_int)
				    (Array.sub !octets 1 3)))
	  | 10 -> Some (Ecart
			  (Array.map (Carte.carte_of_int)
				     (Array.sub !octets 1 3)))
	  | 11 -> Some (Atout
			  (Array.to_list
			     (Array.map (Carte.carte_of_int)
					(Array.sub !octets 1 
						   (Array.length !octets - 1)))))
	  | 13 -> Some Jeu
	  | 16 -> Some (Requete (Carte.carte_of_int (!octets).(1)))
	  | 17 -> Some (Carte (Carte.carte_of_int (!octets).(1)))
	  | 18 -> Some (Pli (!octets).(1))
	  | 19 -> Some (Resultat 
			  (Array.init
			     5 (fun i ->
				let ocsign = (!octets).(2 * i + 1)
					     land 0b11111111 in
				let oc2 = (!octets).(2 * (i + 1)) in
				if ocsign land 0b10000000 <> 0 then
				  (* Score négatif *)
				  let abs1 = 256 - ocsign in
				  let abs2 = (256 - oc2) land 0b11111111 in
				  let abs = (abs1 lsl 8) lor abs2 in
				  -abs
				else (ocsign lsl 8) lor oc2)))
	  | 20 -> Some (Identifier (mk_string 1))
	  | 21 -> Some (Entree (mk_string 1))
	  | 22 -> Some (Sortie (mk_string 1))
	  | 23 -> Some (Noms (Array.init 5 (fun i -> mk_string (16 * i + 1))))
	  | 24 -> Some (Inviter (Array.init 4 (fun i -> mk_string (16 * i + 1))))
	  | _ -> Some ErreurProtocole
	with Invalid_argument "index out of bounds" -> 
	Some ErreurProtocole
      in
      let () = octets := [||] in
      reponse
    else None (* Le message n'est pas terminé *)
  in
  ajouter_entier
